const path = require("path");
const { merge } = require("webpack-merge"); // merge 函数
const { Configuration, DefinePlugin } = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const Analyzer = require("webpack-bundle-analyzer");
const os = require("os");
// cpu核数
const threads = os.cpus().length;

// webpack优化
// https://blog.csdn.net/qq_53270554/article/details/137349970

/**
 * @type { Configuration }
 */
module.exports = (args, config) => {
  const envTags = (process.env.TAGS || "").split(",");
  const isDev = config.mode == "development";
  return {
    entry: {
      vue: ["vue"],
      main: "./src/main.js",
      app: "./src/app.js",
    },
    mode: config.mode,
    output: {
      clean: true, // 清空dist目录
      path: path.join(__dirname, "dist"), // 输出目录
      // publicPath: "./", // html页面中请求文件的相对路径，注意这里设置的publicPath，要同步设置devServer中publicPath
      // chunkFilename: "js/[name]-[chunkhash].js", // 异步chunk文件名，通常不用设置，统一用filename即可，这里只是演示和filename的不同
      filename: "js/[name]-[chunkhash].js", // 多入口打包
      assetModuleFilename: "images/[hash].[name][ext][query]", // 通用assets资源名
    },
    // cache: {
    //   // 缓存使用文件系统缓存，或者默认true，注意如果修改第三方包或者webpack配置，要重新修改version版本号才能重新生成缓存
    //   type: "filesystem",
    //   version: "2024.12.15",
    // },
    resolve: {
      /* 别名 */
      alias: {
        "@": path.resolve(__dirname, "src"),
      },
      /* 省略扩展名 */
      extensions: [".tsx", ".jsx", ".ts", ".js", ".json", ".vue"],
    },
    /* 热更新服务器和代理 */
    devServer: {
      // publicPath: "./", // html页面中请求文件的相对路径，这里要与output中publicPath一致
      hot: true,
      open: true,
      port: 9000,
      proxy: [
        {
          context: ["/api"],
          target: "http://localhost:3000",
          pathRewrite: { "^/api": "/newapi" }, // 将 /api/users 的请求会将请求代理到 http://localhost:3000/newapi/users，如不重写newapi，可去掉这句
        },
      ],
    },
    // devtool: "source-map",
    optimization: {
      splitChunks: {
        minSize: 1, // 表示在压缩前的最小模块大小，默认为 20000，如果不设置那么node_modules中小于minSize的不会打入vendors包
        chunks: "all", // 默认是async，则默认规则只应用到异步chuank，设置all就包含 初始chunk、异步chuank
        cacheGroups: {
          vendors: {
            test: /node_modules/,
            name: "vendors",
            priority: 0,
          },
          // vue: {
          //   test: /vue/,
          //   name: "vue",
          //   priority: 10,
          // },
          lodash: {
            test: /lodash/,
            name: "lodash",
            priority: 10,
          },
        },
      },
    },
    plugins: [
      /** vue插件 */
      new VueLoaderPlugin(),
      /** 全局变量 */
      new DefinePlugin({
        PRODUCTION: true, // 前台直接使用不用引入编译时会替换，如 console.log("PRODUCTION", PRODUCTION);
        SERVICE_URL: JSON.stringify("https://dev.example.com"),
      }),
      /** 静态拷贝文件 */
      new CopyPlugin({
        patterns: [{ from: "public", to: "." }],
      }),
      /** 生成html文件 */
      new HtmlWebpackPlugin({
        title: "测试标题",
        template: "index.html",
      }),
      new HtmlWebpackPlugin({
        title: "测试标题仅app",
        template: "index.html",
        chunks: ["app"], // 绑定的入口chunks列表，可不填是所有入口都
        filename: "app.html", //生成html文件名
      }),
      /** 处理css文件 */
      new MiniCssExtractPlugin({
        filename: "css/[name]-[chunkhash].css",
        ignoreOrder: true,
      }),
      /** 依赖分析 */
      envTags.includes("analyzer") ? new Analyzer.BundleAnalyzerPlugin() : null,
    ].filter(Boolean),
    module: {
      rules: [
        /* vue文件 */
        // vue-loader要放首位，不然报错 Error: [VueLoaderPlugin Error] No matching use for vue-loader is found.
        // 解决 https://blog.csdn.net/mmiaoChong/article/details/120417930
        {
          test: /\.vue$/,
          use: ["vue-loader"],
        },
        /** 处理css文件 */
        {
          test: /\.(css|scss)$/i,
          use: [
            isDev ? "style-loader" : MiniCssExtractPlugin.loader, // 开发模式要style-loader，不然特更新会样式丢失
            /*将 CSS 转化成 CommonJS 模块*/
            "css-loader",
            "postcss-loader",
            "sass-loader",
          ],
          /**
           * 这里处理css有两种方式：
           * 1、用 use: [MiniCssExtractPlugin.loader, "css-loader"],则css文件会单独生成css文件并在html中直接link方式引入样式文件
           * 2、用 use: ["style-loader", "css-loader"], 并且在plugins里面注释掉 new MiniCssExtractPlugin 不用插件
           *    则不会单独生成css文件，而且style样式写如js文件通过js生成style样式标签插入到html的head头里面
           */
        },
        /** 处理html*/
        {
          test: /\.html$/,
          use: ["text-loader"],
        },
        /** 处理字体等文件 */
        {
          test: /\.(eot|svg|ttf|woff|woff2|)$/,
          type: "asset/resource", // asset/resource 外链字体（测试字体默认是外链，可以这里不设置ttf）
          generator: {
            filename: "fonts/[hash].[name][ext][query]",
          },
        },
        /** 处理图片等文件 */
        {
          test: /\.(jpg|jpeg|gif|webp|png|svg)$/,
          type: "asset", // asset 资源类型可以根据指定的图片大小来判断是否需要转化为 base64
          parser: {
            dataUrlCondition: {
              maxSize: 10 * 1024, // 限制于 10kb 以下用Base64，以上用url路径
            },
          },
        },
        {
          test: /\.ico$/,
          type: "asset/inline", // 内链Base64图片
        },
        /* babel处理 */
        {
          test: /\.(ts|js|jsx|tsx)$/,
          use: [
            {
              loader: "thread-loader", // 开启多进程
              options: {
                workers: threads, // 数量
              },
            },
            {
              loader: "babel-loader",
              options: {
                // 每次打包时 js文件都要经过Eslint检查 和Babel编译，速度比较慢。
                // 我们可以缓存之前的 Eslint 检查 和Babel编译结果，这样第二次打包时速度就会更快了
                cacheDirectory: true, // 缓存，在node_modules下增加.cache缓存目录
                cacheCompression: false, // 缓存文件不要压缩
              },
            },
          ],
          include: path.resolve(__dirname, "src"), // 也可以用包含，与下面exclude二选一
          // exclude: /node_modules/, //排除 node_modules 目录，否则会报package.json中没有配置 "type": "module"
        },
      ],
    },
  };
};
